
#
# Plyaer with Web Audio API
#
class W3CPlayer extends Player
  context: null
  stream_length: 4096
  subscriber_node: null
  publisher_node: null
  subscriber_buffer: null # Float32Array
  publisher_buffer: null  # Float32Array
  channel_merger: null

  # check Web Audio API available
  @available_api:-> webkitAudioContext?

  #
  # constructor
  #
  constructor:(controller)->
    super(controller)
    @context = new webkitAudioContext()
    @publisher_node = @context.createJavaScriptNode(@stream_length, 
      1, @context.destination.numberOfChannels)
    @subscriber_node = @context.createJavaScriptNode(@stream_length, 
      1, @context.destination.numberOfChannels)
    @channel_merger = @context.createChannelMerger()
    console.log "W3CPlayer::Constructor rate=#{@context.sampleRate} channel=#{@context.destination.numberOfChannels}"

    # on audio proccess
    # called each buffer loaded (buffer size = @stream_length)
    @publisher_node.onaudioprocess = (audio_event)=>
      pcm_block_allch = []

      # take each block(@stream_length size) of all channels into flatten array
      for i in [0...@publisher_buffer.length]
        pcm_block_allch.push(@publisher_buffer[i].shift())

      # broadcast audio packet
      @controller.send_audio_stream(pcm_block_allch)

      console.log pcm_block_allch

      # check remains of buffer to play
      if @publisher_buffer[0].length is 0
        @finish_publisher_node()
        return

      # set output buffer @todo enable
      #for ch in [0...pcm_block_allch.length]
      #  audio_event.outputBuffer.getChannelData(ch).set(pcm_block_allch[ch])
      return

    # on audio proccess for subscribers
    # called each buffer received
    @subscriber_node.onaudioprocess = (audio_event)=>
      pcm_block_allch = []
      # take each block(@stream_length size) of all channels into flatten array
      if !@subscriber_buffer? then return
      
      if @subscriber_buffer.length > 0
        allch = @subscriber_buffer.shift()
        for ch in [0...allch.length]
          pcm_block_allch.push(allch[ch])

      for ch in [0...pcm_block_allch.length]
        audio_event.outputBuffer.getChannelData(ch).set(pcm_block_allch[ch])
      return
      
    return

  #
  # start play
  #
  start_play_as_publisher:(tune, on_load_finished)->
    # start request
    @set_state('playready')
    @tune_list.tune_on_playing = tune
    @tune_list.tune_on_playing.requester_session_id = @controller.user_me.session_id
    @controller.send_start_playing_request(tune)

    # read handler
    reader = new FileReader()
    reader.onerror = (e)=>
      # @todo handling error
      console.log('Error occured in File Reader')
      throw new Error('FileReader error :' + e.target.error.code)
      return
    
    reader.onload = (e)=>
      @context.decodeAudioData e.target.result, (buffer)=>
        # decode 
        # (ex) 2 channels
        # [0] - left
        #   [0][0] block #0 on channel 0
        #     [0][0][0 to @stream_length] packet #0 on channel 0
        #   [0][1] block #1 on channel 0
        #     [0][1][0 to @stream_length] packet #1 on channel 0
        #   ....
        # [1] - right
        #   [1][0] block #0 on channel 1
        #     [0][0][0 to @stream_length] packet #0 on channel 1
        #   ....

        #console.log('start decoding')
        pcm_buffer = []
        for ch in [0...buffer.numberOfChannels]
          pcm_buffer[ch] = [];
          for i in [0...buffer.length]
            block = ~~(i/@stream_length)
            offset = i%@stream_length
            if offset is 0
              pcm_buffer[ch][block] = new Float32Array(@stream_length)
            pcm_buffer[ch][block][offset] = buffer.getChannelData(ch)[i]
        @publisher_buffer = pcm_buffer
        

        # stop subscriber
        @stop_subscriber_node()

        # notify to UI that audios to be playing
        @set_state('playing')
        @controller.msg_started_play(@tune_list.tune_on_playing)

        # start 'onaudioprocess' cycle
        @publisher_node.connect(@context.destination)
        return
      return

    # do read audio file
    reader.readAsArrayBuffer(tune.file);
    console.log('start as publisher : ' + tune.file.name)
    return

  #
  # stop subscriber
  #
  stop_publisher_node:()->
    console.log('publisher has stopped.')
    @publisher_node.disconnect()
    @set_state('idle')
    @tune_list.tune_on_playing = null
    return

  #
  # stop publisher
  #
  finish_publisher_node:()->
    @controller.send_finish_publishing(@tune_list.tune_on_playing)
    @stop_publisher_node()
    @controller.msg_finished_publisher()
    return

    #@todo request next music (stat request next tune in playlist)

  #
  # start listen
  #
  start_listen_as_subscriber:(tune)->
    console.log('start_listen_as_subscriber.')
    @stop_publisher_node()

    @set_state('listening')
    @tune_list.tune_on_playing = tune
    @subscriber_buffer = []
    
    # start 'onaudioprocess' cycle
    @subscriber_node.connect(@context.destination)
    return

  #
  # on receive packet
  #
  on_receive_stream:(pcm_buffer)->
    #@todo convert chunnel and rate
    allch = []
    for ch in [0...pcm_buffer.length]
      allch.push pcm_buffer[ch]
    
    @subscriber_buffer.push(allch)
    #console.log @subscriber_buffer
    return

  #
  # stop subscriber
  #
  stop_subscriber_node:()->
    @subscriber_node.disconnect()
    @set_state('idle')
    return
